package com.codeanalysissys.backend.ruby;


import ghaffarian.graphs.Edge;
import ghaffarian.nanologger.Logger;

import com.codeanalysissys.backend.graphs.cfg.ControlFlowGraph;

import java.lang.*;


import com.codeanalysissys.backend.ruby.parser.RubyLexer;
import com.codeanalysissys.backend.ruby.parser.RubyParser;

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import com.codeanalysissys.backend.graphs.cfg.CFEdge;
import com.codeanalysissys.backend.graphs.cfg.CFNode;
import com.codeanalysissys.backend.ruby.parser.RubyParserBaseVisitor;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.*;

public class RubyCFGBuilder {
    public static ControlFlowGraph build(InputStream inFile) throws IOException {
        // 将字符串转换为字节数组
//        byte[] bytes = content.getBytes();
        // 使用 ByteArrayInputStream 封装字节数组为输入流
//        InputStream inFile = new ByteArrayInputStream(bytes);

        ANTLRInputStream input = new ANTLRInputStream(inFile);
        RubyLexer lexer = new RubyLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        RubyParser parser = new RubyParser(tokens);
        ParseTree tree = parser.compilation_unit();
        return build(tree, null, null);
    }
    public static ControlFlowGraph build(ParseTree tree, String propKey, Map<ParserRuleContext, Object> ctxProps) {
        ControlFlowGraph cfg = new ControlFlowGraph();
        ControlFlowVisitor visitor = new ControlFlowVisitor(cfg, propKey, ctxProps);
        visitor.visit(tree);
        return cfg;
    }
    public static ControlFlowGraph build(String cppFile) throws IOException {
        return build(new File(cppFile));
    }
    public static ControlFlowGraph build(File rubyFile) throws IOException {
        if (!rubyFile.getName().endsWith(".rb"))
            throw new IOException("Not a ruby File!");
        //InputStream inFile = new FileInputStream(cppFile);
        // ANTLRInputStream input = new ANTLRInputStream(inFile);
        CharStream input = CharStreams.fromPath(Paths.get(rubyFile.getAbsolutePath()));
        RubyLexer  lexer = new RubyLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        RubyParser parser = new RubyParser(tokens);
        ParseTree tree = parser.compilation_unit();
        //System.out.print(tree);
        return build(rubyFile.getName(), tree, null, null);
    }
    public static ControlFlowGraph build(String fileName, InputStream inputStream) throws IOException {

        InputStream inFile = inputStream;
        ANTLRInputStream input = new ANTLRInputStream(inFile);
        RubyLexer lexer = new RubyLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        RubyParser parser = new RubyParser(tokens);
        ParseTree tree = parser.compilation_unit();
        return build(fileName, tree, null, null);
    }
    public static ControlFlowGraph build(String rubyFileName, ParseTree tree,
                                         String propKey, Map<ParserRuleContext, Object> ctxProps) {
        ControlFlowGraph cfg = new ControlFlowGraph(rubyFileName);
        ControlFlowVisitor visitor = new ControlFlowVisitor(cfg, propKey, ctxProps);
        visitor.visit(tree);
        return cfg;
    }

    private static class ControlFlowVisitor extends RubyParserBaseVisitor<Void> {
        private ControlFlowGraph cfg;

        private Deque<CFNode> preNodes;
        private Deque<CFEdge.Type> preEdges;
        private Deque<CFNode> raise;
        private Deque<RubyCFGBuilder.ControlFlowVisitor.Block> loopBlocks;
        private List<RubyCFGBuilder.ControlFlowVisitor.Block> labeledBlocks;
        private Deque<RubyCFGBuilder.ControlFlowVisitor.Block> tryBlocks;
        private Queue<CFNode> casesQueue;
        private boolean dontPop;
        private String propKey;
        private Map<ParserRuleContext, Object> contexutalProperties;
        private Deque<String> classNames;
        private String expr;
        private boolean Done;

        private int end;//获取end所在的行数
        private CFNode END; //防止多个end节点
        private Deque<CFNode> NEXT;
        private Deque<CFNode> BREAK;
        private List<CFNode> CFYield, endyield;
        private boolean re;
        private boolean isBreak = false;
        private boolean isreturn = false;
        private boolean israise = false;
        private boolean isfor;
        private Deque<String> method_name;

        public ControlFlowVisitor(ControlFlowGraph cfg, String propKey, Map<ParserRuleContext, Object> ctxProps) {
            preNodes = new ArrayDeque<>();
            preEdges = new ArrayDeque<>();
            loopBlocks = new ArrayDeque<>();
            labeledBlocks = new ArrayList<>();
            tryBlocks = new ArrayDeque<>();
            casesQueue = new ArrayDeque<>();
            classNames = new ArrayDeque<>();
            CFYield = new ArrayList<>();
            endyield = new ArrayList<>();
            method_name=new ArrayDeque<>();
            raise=new ArrayDeque<>();
            dontPop = false;
            expr="";
            Done=false;
            end=0;
            re=false;
            isfor=false;
            BREAK=new ArrayDeque<>();;
            this.cfg = cfg;
            //
            NEXT=new ArrayDeque<>();;
            this.propKey = propKey;
            contexutalProperties = ctxProps;
        }

        private void init() {
//            preNodes.clear();
//            preEdges.clear();
            BREAK.clear();
            NEXT.clear();
            loopBlocks.clear();
            labeledBlocks.clear();
            tryBlocks.clear();
            dontPop = false;
            expr="";
            Done=false;
            end=0;
            isfor=false;
            re=false;
        }

        private void addContextualProperty(CFNode node, ParserRuleContext ctx) {
            if (propKey != null && contexutalProperties != null) {
                Object prop = contexutalProperties.get(ctx);
                if (prop != null)
                    node.setProperty(propKey, prop);//添加pdnode属性
            }
        }
        @Override
        public Void visitCompilation_unit(RubyParser.Compilation_unitContext ctx)
        {
            CFNode main=new CFNode();
            main.setLineOfCode(0);
            main.setCode("start");
            cfg.addVertex(main);
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(main);
            cfg.addMethodEntry(main);
            String a=ctx.getText();
            visitChildren(ctx);
            if(!expr.equals("")) {
                CFNode exp = new CFNode();
                exp.setLineOfCode(ctx.EOF().getSymbol().getLine());
                exp.setCode(expr);
                if (preEdges.isEmpty()) {
                    cfg.addVertex(exp);
                } else {
                    addNodeAndPreEdge(exp);
                }
            }
            return null;
        }
        @Override
        public Void visitPrimaryBlockClassDef(RubyParser.PrimaryBlockClassDefContext ctx)
        {
            if(ctx.class_definition().class_header().cpath()!=null) {
                classNames.push(getOriginalCodeText(ctx.class_definition().class_header().cpath()));
                visit(ctx.class_definition());
                classNames.pop();
            }
            else {
                classNames.push(getOriginalCodeText(ctx.class_definition().class_header().identifier()));
                visit(ctx.class_definition());
                classNames.pop();
            }
            return null;
        }
        @Override
        public Void visitClass_definition(RubyParser.Class_definitionContext ctx)
        {
                Deque<CFEdge.Type> tempEdges= new ArrayDeque<>();
                tempEdges.addAll(preEdges);
                preEdges.clear();
                init();

                CFNode entry = new CFNode();                         //构建类头节点
                entry.setLineOfCode(ctx.getStart().getLine());
                entry.setCode(getOriginalCodeText(ctx.class_header()));
                addContextualProperty(entry,ctx);
                cfg.addVertex(entry);
                cfg.addMethodEntry(entry);
                entry.setProperty("class", classNames.peek());
                preNodes.push(entry);
              preEdges.push(CFEdge.Type.EPSILON);


                visit(ctx.statement_list_terms());
                expNode(ctx.statement_list_terms());

                CFNode endclass=new CFNode();
                endclass.setLineOfCode(ctx.END().getSymbol().getLine());
                endclass.setCode("endclass");
                endclass.setProperty("class", classNames.peek());
                if(!preEdges.isEmpty())
                    addNodeAndPreEdge(endclass);
                tempEdges.addAll(preEdges);
                preEdges.clear();
                preEdges.addAll(tempEdges);
                tempEdges.clear();
                Done=true;
            return null;
        }
        @Override
        public Void visitFunction_definition(RubyParser.Function_definitionContext ctx)
        {
            Deque<CFEdge.Type> tempEdges= new ArrayDeque<>();
            tempEdges.addAll(preEdges);
            preEdges.clear();
            init();
            CFNode entry = new CFNode();
            entry.setLineOfCode(ctx.getStart().getLine());
            String tmp=getOriginalCodeText(ctx.function_definition_header());
            tmp=tmp.replace("\r\n","");
            String retType = "auto";
            if (ctx.function_definition_header().expr() != null) {
                entry.setCode(tmp + getOriginalCodeText(ctx.function_definition_header().expr()));
                retType = ctx.function_definition_header().expr().getText();
                retType=retType.replace("->","");
            }

            else
                entry.setCode(tmp);
            addContextualProperty(entry, ctx);
            cfg.addVertex(entry);
            method_name.push(getOriginalCodeText(ctx.function_definition_header().function_name()));

                 // set return type
            entry.setProperty("type",retType);
            entry.setProperty("name", method_name.peek());
            if(!classNames.isEmpty())
                entry.setProperty("class", classNames.peek());
            cfg.addMethodEntry(entry);
            preNodes.push(entry);
            preEdges.push(CFEdge.Type.EPSILON);
            if(ctx.statement_list_terms().statement()!=null)
            {
                visit(ctx.statement_list_terms());
                if(re==false)
                {
                        CFNode exp = new CFNode();
                        CFNode endmethod = new CFNode();
                        endmethod.setLineOfCode(0);
                    if(!expr.equals("")) {
                        exp.setLineOfCode(ctx.statement_list_terms().terms().getStart().getLine());
                        exp.setCode(expr);
                        addContextualProperty(exp,ctx.statement_list_terms());
                        addNodeAndPreEdge(exp);
                        preNodes.push(exp);
                        preEdges.push(CFEdge.Type.EPSILON);
                        endmethod.setCode("return " +expr);
                        addNodeAndPreEdge(endmethod);
                        expr="";
                    }
                    else {
                        exp=preNodes.pop();
                        endmethod.setCode("return " + exp.getCode());
                        preNodes.push(exp);
                        addNodeAndPreEdge(endmethod);
                    }
                }
            }
            else {
                CFNode endmethod = new CFNode();
                endmethod.setLineOfCode(0);
                endmethod.setCode("return nil");
                addNodeAndPreEdge(endmethod);
            }
            tempEdges.addAll(preEdges);
            preEdges.clear();
            preEdges.addAll(tempEdges);
            tempEdges.clear();
            re=false;
            method_name.pop();
            Done=true;
            return null;
        }
        @Override
        public Void visitModule_definition(RubyParser.Module_definitionContext ctx)
        {
           Deque<CFEdge.Type> tempEdges= new ArrayDeque<>();
            tempEdges.addAll(preEdges);
            preEdges.clear();
            init();
            CFNode entry = new CFNode();                         //构建类头节点
            entry.setLineOfCode(ctx.getStart().getLine());
            entry.setCode("module "+getOriginalCodeText(ctx.cpath()));
            classNames.push(getOriginalCodeText(ctx.cpath()));
            addContextualProperty(entry,ctx);
            cfg.addVertex(entry);
            cfg.addMethodEntry(entry);
            entry.setProperty("module", classNames.peek());
            preNodes.push(entry);
            preEdges.push(CFEdge.Type.EPSILON);
            visit(ctx.statement_list_terms());
            CFNode endclass=new CFNode();
            endclass.setLineOfCode(ctx.END().getSymbol().getLine());
            endclass.setCode("endmodule");
            endclass.setProperty("module", classNames.peek());
            if(!preEdges.isEmpty())
                addNodeAndPreEdge(endclass);
            Done=true;
            tempEdges.addAll(preEdges);
            preEdges.clear();
            preEdges.addAll(tempEdges);
            tempEdges.clear();
            return null;
        }
        @Override
        public Void visitExprDotRef(RubyParser.ExprDotRefContext ctx)
        {
            isfor=true;
            return null;
        }
        @Override
        public Void visitPrimaryStatementReturn(RubyParser.PrimaryStatementReturnContext ctx)
        {
            isreturn=true;
//            CFNode endmethod = new CFNode();
//            endmethod.setLineOfCode(ctx.getStart().getLine());
//            if(ctx.expr()!=null)
//                endmethod.setCode("return "+getOriginalCodeText(ctx.expr()));
//            else
//                endmethod.setCode("return nil");
//            addNodeAndPreEdge(endmethod);

//            Done=true;

            re=true;
            return null;
        }
        @Override
        public Void visitPrimaryBlockWhile(RubyParser.PrimaryBlockWhileContext ctx)
        {

            CFNode FOR=new CFNode();
            FOR.setLineOfCode(ctx.getStart().getLine());
            String whileexp="while "+getOriginalCodeText(ctx.expr());
            if(ctx.do_keyword().SEMICOLON()!=null)
                whileexp+=" ;";
            if(ctx.do_keyword().DO()!=null)
                whileexp+=" do";
            FOR.setCode(whileexp);
            addContextualProperty(FOR, ctx);
            if(preEdges.isEmpty())
            {
                cfg.addVertex(FOR);
                cfg.addMethodEntry(FOR);
            }
            else
            {
                addNodeAndPreEdge(FOR);
            }
            preEdges.push(CFEdge.Type.TRUE);
            preNodes.push(FOR);
            CFNode endfor =new CFNode();
            endfor.setLineOfCode(ctx.END().getSymbol().getLine());
            endfor.setCode("endwhile");
            END=endfor;
            cfg.addVertex(END);
            if(ctx.statement_list_terms()!=null) {
                visit(ctx.statement_list_terms());
                expNode(ctx.statement_list_terms());
                END=endfor;
            }
            if(!preEdges.isEmpty())
            popAddPreEdgeTo(FOR);

            cfg.addEdge(new Edge<>(FOR, new CFEdge(CFEdge.Type.FALSE), END));
            Done=true;
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(END);
            if(!NEXT.isEmpty())
            {
                for(int i=0;i<NEXT.size();i++)
                    cfg.addEdge(new Edge<>(NEXT.pop(), new CFEdge(CFEdge.Type.EPSILON), FOR));

            }
            if(!BREAK.isEmpty()) {
                cfg.addEdge(new Edge<>(BREAK.pop(), new CFEdge(CFEdge.Type.EPSILON), endfor));
            }
            return null;
        }
        @Override
        public Void visitPrimaryStatementYield(RubyParser.PrimaryStatementYieldContext ctx)
        {
            CFNode nowyield=new CFNode();
            nowyield=new CFNode();
            nowyield.setLineOfCode(ctx.getStart().getLine());
            nowyield.setCode("yield");
            nowyield.setProperty("class",method_name.peek());
            addContextualProperty(nowyield,ctx);
            addNodeAndPreEdge(nowyield);
            CFYield.add(nowyield);
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(nowyield);
            CFNode nowendyield=new CFNode();
            nowendyield.setCode("endyield");
            nowendyield.setLineOfCode(0);
            nowendyield.setProperty("class",method_name.peek());
            addNodeAndPreEdge(nowendyield);
            cfg.addVertex(nowendyield);
            endyield.add(nowendyield);
            preNodes.push(nowendyield);
            preEdges.push(CFEdge.Type.EPSILON);
            Done=true;
            return null;
        }
        @Override
        public Void visitBlock(RubyParser.BlockContext ctx)
        {

            CFNode enditerator=new CFNode();
            enditerator.setLineOfCode(ctx.RIGHT_PAREN().getSymbol().getLine());
            enditerator.setCode("endblock");
            END=enditerator;
            cfg.addVertex(END);
                if(ctx.block_params()!=null) {
                    CFNode params=new CFNode();
                    params.setLineOfCode(ctx.block_params().getStart().getLine());
                    params.setCode(getOriginalCodeText(ctx.block_params()));
                    addNodeAndPreEdge(params);
                    addContextualProperty(params, ctx.block_params());
                    preEdges.push(CFEdge.Type.EPSILON);
                    preNodes.push(params);
                }
                if(ctx.statement_list_terms()!=null) {
                    visit(ctx.statement_list_terms());
                    expNode(ctx.statement_list_terms());
                    END=enditerator;
                }

            return null;
        }
        @Override
        public Void visitExprBlock(RubyParser.ExprBlockContext ctx)
        {
            CFNode FOR=new CFNode();
            FOR.setLineOfCode(ctx.getStart().getLine());
            FOR.setCode(getOriginalCodeText(ctx.expr()));
            addContextualProperty(FOR, ctx.expr());
            if(preEdges.isEmpty())
            {
                cfg.addVertex(FOR);
                cfg.addMethodEntry(FOR);
            }
            else
            {
                addNodeAndPreEdge(FOR);
            }
            preEdges.push(CFEdge.Type.TRUE);
            preNodes.push(FOR);
            visit(ctx.block());
            if(!preEdges.isEmpty())
            popAddPreEdgeTo(FOR);

            cfg.addEdge(new Edge<>(FOR, new CFEdge(CFEdge.Type.FALSE), END));
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(END);
            Done=true;
            return null;
        }
        @Override
        public Void visitExpr_statement_suffix(RubyParser.Expr_statement_suffixContext ctx)
        {
            if(ctx.DO()!=null)
            {
                if(isfor) {
                    CFNode enditerator = new CFNode();
                    enditerator.setLineOfCode(ctx.END().getSymbol().getLine());
                    enditerator.setCode("enditerator");
                    END = enditerator;
                    cfg.addVertex(END);
                    CFNode params = new CFNode();
                    params.setLineOfCode(ctx.block_params().getStart().getLine());
                    params.setCode(getOriginalCodeText(ctx.block_params()));
                    addNodeAndPreEdge(params);
                    addContextualProperty(params, ctx.block_params());
                    preEdges.push(CFEdge.Type.EPSILON);
                    preNodes.push(params);
                    if (ctx.statement_list_terms() != null) {
                        visit(ctx.statement_list_terms());
                        expNode(ctx.statement_list_terms());
                        END = enditerator;
                    }

                }
                else
                {
                    CFNode endblock = new CFNode();
                    endblock.setLineOfCode(ctx.END().getSymbol().getLine());
                    endblock.setCode("endcode");
                    END = endblock;
                    cfg.addVertex(END);
                    if (ctx.statement_list_terms() != null) {
                        visit(ctx.statement_list_terms());
                        expNode(ctx.statement_list_terms());
                        END = endblock;
                    }
                    if(!preEdges.isEmpty())
                        popAddPreEdgeTo(END);

                }
            }

            else if(ctx.UNTIL()!=null)
            {
                CFNode enduntil=new CFNode();
                enduntil.setLineOfCode(0);
                enduntil.setCode("enduntil");
                END=enduntil;
                CFNode untilexp=new CFNode();
                untilexp.setLineOfCode(ctx.getStart().getLine());
                untilexp.setCode("until "+getOriginalCodeText(ctx.expr()));
                addContextualProperty(untilexp,ctx);
                if(preEdges.isEmpty())
                {
                    cfg.addVertex(untilexp);
                    cfg.addMethodEntry(untilexp);
                }
                else
                {
                    addNodeAndPreEdge(untilexp);
                }
                //preEdges.push(CFEdge.Type.FALSE);
                preNodes.push(untilexp);
                cfg.addVertex(END);
                cfg.addEdge(new Edge<>(untilexp, new CFEdge(CFEdge.Type.TRUE), END));
            }
            else if(ctx.WHILE()!=null)
            {
                CFNode endwhile=new CFNode();
                endwhile.setLineOfCode(0);
                endwhile.setCode("endwhile");
                END=endwhile;
                CFNode whileexp=new CFNode();
                whileexp.setLineOfCode(ctx.getStart().getLine());
                whileexp.setCode("while "+getOriginalCodeText(ctx.expr()));
                addContextualProperty(whileexp,ctx);
                if(preEdges.isEmpty())
                {
                    cfg.addVertex(whileexp);
                    cfg.addMethodEntry(whileexp);
                }
                else
                {
                    addNodeAndPreEdge(whileexp);
                }
                //preEdges.push(CFEdge.Type.TRUE);
                preNodes.push(whileexp);
                cfg.addVertex(END);
                cfg.addEdge(new Edge<>(whileexp, new CFEdge(CFEdge.Type.FALSE), END));
            }

            else if(ctx.IF()!=null)
            {
                CFNode endif=new CFNode();
                endif.setLineOfCode(0);
                endif.setCode("endif");
                END=endif;
                CFNode ifexp=new CFNode();
                ifexp.setLineOfCode(ctx.getStart().getLine());
                ifexp.setCode("if "+getOriginalCodeText(ctx.expr()));
                addContextualProperty(ifexp,ctx);
                if(preEdges.isEmpty())
                {
                    cfg.addVertex(ifexp);
                    cfg.addMethodEntry(ifexp);
                }
                else
                {
                    addNodeAndPreEdge(ifexp);
                }
                preEdges.push(CFEdge.Type.TRUE);
                preNodes.push(ifexp);
                cfg.addVertex(END);
                cfg.addEdge(new Edge<>(ifexp, new CFEdge(CFEdge.Type.FALSE), END));
            }
            else if(ctx.UNLESS()!=null)
            {
                CFNode endless=new CFNode();
                endless.setLineOfCode(0);
                endless.setCode("endunless");
                END=endless;
                CFNode unlessexp=new CFNode();
                unlessexp.setLineOfCode(ctx.getStart().getLine());
                unlessexp.setCode("unless "+getOriginalCodeText(ctx.expr()));
                addContextualProperty(unlessexp,ctx);
                if(preEdges.isEmpty())
                {
                    cfg.addVertex(unlessexp);
                    cfg.addMethodEntry(unlessexp);
                }
                else
                {
                    addNodeAndPreEdge(unlessexp);
                }
                preEdges.push(CFEdge.Type.FALSE);
                preNodes.push(unlessexp);
                cfg.addVertex(END);
                cfg.addEdge(new Edge<>(unlessexp, new CFEdge(CFEdge.Type.TRUE), END));
            }
            Done=true;
            return null;
        }
        @Override
        public Void visitExprWitStatementSuffix(RubyParser.ExprWitStatementSuffixContext ctx)
        {
            if(ctx.expr_statement_suffix().DO()!=null) {
                visit(ctx.expr());
                if(isfor) {
                    CFNode FOR = new CFNode();
                    FOR.setLineOfCode(ctx.getStart().getLine());
                    FOR.setCode(getOriginalCodeText(ctx.expr()));
                    addContextualProperty(FOR, ctx.expr());
                    if (preEdges.isEmpty()) {
                        cfg.addVertex(FOR);
                        cfg.addMethodEntry(FOR);
                    } else {
                        addNodeAndPreEdge(FOR);
                    }
                    preEdges.push(CFEdge.Type.TRUE);
                    preNodes.push(FOR);
                    visit(ctx.expr_statement_suffix());
                    if(!preEdges.isEmpty())
                    popAddPreEdgeTo(FOR);

                    cfg.addEdge(new Edge<>(FOR, new CFEdge(CFEdge.Type.FALSE), END));
                    preEdges.push(CFEdge.Type.EPSILON);
                    preNodes.push(END);
                    Done = true;
                    isfor=false;
                    if(!NEXT.isEmpty())
                    {
                        for(int i=0;i<NEXT.size();i++)
                            cfg.addEdge(new Edge<>(NEXT.pop(), new CFEdge(CFEdge.Type.EPSILON), FOR));

                    }
                    if(!BREAK.isEmpty()) {
                        cfg.addEdge(new Edge<>(BREAK.pop(), new CFEdge(CFEdge.Type.EPSILON), END));
                    }
                }
                else       //不是for
                {
                    String cla=getOriginalCodeText(ctx.expr());
                    CFNode exprss=new CFNode();
                    exprss.setLineOfCode(ctx.expr().getStart().getLine());
                    exprss.setCode(cla);
                    addContextualProperty(exprss,ctx);
                    if(preEdges.isEmpty())
                    {
                        cfg.addVertex(exprss);
                        cfg.addMethodEntry(exprss);
                    }
                    else
                    {
                        addNodeAndPreEdge(exprss);
                    }
                    preNodes.push(exprss);
                    preEdges.push(CFEdge.Type.EPSILON);
                    for (int i = 0; i < CFYield.size(); i++) {
                        CFNode name = CFYield.get(i);
                        if (name.getProperty("class").equals(cla)) {
                            CFNode startyield = new CFNode();
                            startyield.setLineOfCode(ctx.expr_statement_suffix().getStart().getLine());
                            startyield.setCode("startyield");
                            cfg.addVertex(startyield);
                            cfg.addEdge(new Edge<>(name, new CFEdge(CFEdge.Type.EPSILON), startyield));
                            preNodes.push(startyield);
                            preEdges.push(CFEdge.Type.EPSILON);
                            visit(ctx.expr_statement_suffix().statement_list_terms());
                            endyield.get(i).setLineOfCode(ctx.expr_statement_suffix().END().getSymbol().getLine());
                            addNodeAndPreEdge(endyield.get(i));
                            Done = true;
                            isfor=false;
                            return null;
                        }
                    }
                    visit(ctx.expr_statement_suffix());
                    Done = true;
                    isfor=false;
                }

            }
            else if(ctx.expr_statement_suffix().UNTIL()!=null)
            {
                visit(ctx.expr_statement_suffix());
                CFNode exp=new CFNode();
                exp.setLineOfCode(ctx.expr().getStart().getLine());
                if(!expr.equals("")) {
                    exp.setCode(expr + " " + getOriginalCodeText(ctx.expr()));
                    expr="";
                }
                else
                    exp.setCode(getOriginalCodeText(ctx.expr()));
                addContextualProperty(exp,ctx.expr());
                CFNode tmp_until=preNodes.pop();
                cfg.addVertex(exp);
                cfg.addEdge(new Edge<>(tmp_until, new CFEdge(CFEdge.Type.FALSE), exp));
                cfg.addEdge(new Edge<>(exp, new CFEdge(CFEdge.Type.EPSILON), tmp_until));
                preEdges.push(CFEdge.Type.EPSILON);
                preNodes.push(END);
                Done = true;
                isfor=false;

            }
            else if(ctx.expr_statement_suffix().WHILE()!=null)
            {
                visit(ctx.expr_statement_suffix());
                CFNode exp=new CFNode();
                exp.setLineOfCode(ctx.expr().getStart().getLine());
                if(!expr.equals("")) {
                    exp.setCode(expr + " " + getOriginalCodeText(ctx.expr()));
                    expr="";
                }
                else
                    exp.setCode(getOriginalCodeText(ctx.expr()));
                addContextualProperty(exp,ctx.expr());
                CFNode tmp_while=preNodes.pop();
                cfg.addVertex(exp);
                cfg.addEdge(new Edge<>(tmp_while, new CFEdge(CFEdge.Type.TRUE), exp));
                cfg.addEdge(new Edge<>(exp, new CFEdge(CFEdge.Type.EPSILON), tmp_while));
                preEdges.push(CFEdge.Type.EPSILON);
                preNodes.push(END);
                Done = true;
                isfor=false;

            }
            else if(ctx.expr_statement_suffix().IF()!=null||ctx.expr_statement_suffix().UNLESS()!=null)
            {
                visit(ctx.expr_statement_suffix());
                if(!getOriginalCodeText(ctx.expr()).equals("break")&&!getOriginalCodeText(ctx.expr()).equals("next")) {
                    CFNode exp = new CFNode();
                    exp.setLineOfCode(ctx.expr().getStart().getLine());
                    if (!expr.equals("")) {
                        exp.setCode(expr + " " + getOriginalCodeText(ctx.expr()));
                        expr = "";
                    } else
                        exp.setCode(getOriginalCodeText(ctx.expr()));
                    addContextualProperty(exp, ctx.expr());
                    addNodeAndPreEdge(exp);
                    preEdges.push(CFEdge.Type.EPSILON);
                    preNodes.push(exp);
                }
                else
                    visit(ctx.expr());
                if(!preEdges.isEmpty()) {
                    popAddPreEdgeTo(END);
                    preEdges.push(CFEdge.Type.EPSILON);
                    preNodes.push(END);
                }
                Done = true;
                isfor=false;

            }

            return null;
        }
        @Override
        public Void visitPrimaryBlockUntil(RubyParser.PrimaryBlockUntilContext ctx)
        {
            CFNode until=new CFNode();
            until.setLineOfCode(ctx.getStart().getLine());
            String untilexp="until "+getOriginalCodeText(ctx.expr());
            if(ctx.do_keyword().SEMICOLON()!=null)
                untilexp+=" ;";
            if(ctx.do_keyword().DO()!=null)
                untilexp+=" do";
            until.setCode(untilexp);
            addContextualProperty(until, ctx.expr());
            if(preEdges.isEmpty())
            {
                cfg.addVertex(until);
                cfg.addMethodEntry(until);
            }
            else
            {
                addNodeAndPreEdge(until);
            }
            preEdges.push(CFEdge.Type.FALSE);
            preNodes.push(until);
            CFNode enduntil =new CFNode();
            enduntil.setLineOfCode(ctx.END().getSymbol().getLine());
            enduntil.setCode("enduntil");
            END=enduntil;
            cfg.addVertex(END);
            if(ctx.statement_list_terms()!=null) {
                visit(ctx.statement_list_terms());
                expNode(ctx.statement_list_terms());
                END=enduntil;
            }
            if(!preEdges.isEmpty())
                popAddPreEdgeTo(until);

            cfg.addEdge(new Edge<>(until, new CFEdge(CFEdge.Type.TRUE), END));
            Done=true;
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(END);
            if(!NEXT.isEmpty())
            {
                for(int i=0;i<NEXT.size();i++)
                    cfg.addEdge(new Edge<>(NEXT.pop(), new CFEdge(CFEdge.Type.EPSILON), until));

            }
            if(!BREAK.isEmpty()) {
                cfg.addEdge(new Edge<>(BREAK.pop(), new CFEdge(CFEdge.Type.EPSILON), enduntil));
            }
            return null;
        }
        @Override
        public Void visitPrimaryBlockFor(RubyParser.PrimaryBlockForContext ctx)
        {
            CFNode FOR=new CFNode();
            FOR.setLineOfCode(ctx.getStart().getLine());
            FOR.setCode("for "+getOriginalCodeText(ctx.expr())+" in "+getOriginalCodeText(ctx.when_cond()));
            addContextualProperty(FOR, ctx);
            if(preEdges.isEmpty())
            {
                cfg.addVertex(FOR);
                cfg.addMethodEntry(FOR);
            }
            else
            {
                addNodeAndPreEdge(FOR);
            }
            preEdges.push(CFEdge.Type.TRUE);
            preNodes.push(FOR);
            CFNode endfor =new CFNode();
            endfor.setLineOfCode(ctx.END().getSymbol().getLine());
            endfor.setCode("endfor");
            END=endfor;
            cfg.addVertex(END);
            if(ctx.statement_list_terms()!=null) {
                visit(ctx.statement_list_terms());
                expNode(ctx.statement_list_terms());
                END=endfor;
            }
            if(!preEdges.isEmpty())
            popAddPreEdgeTo(FOR);
            cfg.addEdge(new Edge<>(FOR, new CFEdge(CFEdge.Type.FALSE), END));
            Done=true;
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(END);
            if(!NEXT.isEmpty())
            {
                for(int i=0;i<NEXT.size();i++)
                    cfg.addEdge(new Edge<>(NEXT.pop(), new CFEdge(CFEdge.Type.EPSILON), FOR));

            }
            if(!BREAK.isEmpty()) {
                cfg.addEdge(new Edge<>(BREAK.pop(), new CFEdge(CFEdge.Type.EPSILON), endfor));
            }
            return null;
        }
        @Override
        public  Void visitPrimaryBlockCase1(RubyParser.PrimaryBlockCase1Context ctx)
        {
            CFNode caseNode = new CFNode();
            caseNode.setLineOfCode(ctx.getStart().getLine());

            if(ctx.statement_list_terms().statement()!=null)
                if(ctx.DOLLAR()!=null)
                    caseNode.setCode("case $"+getOriginalCodeText(ctx.statement_list_terms().statement()));
                else
                    caseNode.setCode("case "+getOriginalCodeText(ctx.statement_list_terms().statement()));
            else
                caseNode.setCode("case");
            addContextualProperty(caseNode, ctx);
            if(preEdges.isEmpty())
            {
                cfg.addVertex(caseNode);
                cfg.addMethodEntry(caseNode);
            }
            else
            {
                addNodeAndPreEdge(caseNode);
            }
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(caseNode);
            CFNode endcase = new CFNode();
            end=ctx.END().getSymbol().getLine();
            endcase.setLineOfCode(end);
            endcase.setCode("endcase");
            END=endcase;
            cfg.addVertex(END);
                for(int i=0;i<ctx.case_body().size();i++)
                {
                    visit(ctx.case_body(i));
                }
            END=endcase;
            Done=true;
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(END);
            return null;
        }
        @Override
        public Void visitCase_body(RubyParser.Case_bodyContext ctx)
        {
            if(ctx.WHEN()!=null)
            {
                CFNode when =new CFNode();
                when.setLineOfCode(ctx.getStart().getLine());
                String whenexp="when "+getOriginalCodeText(ctx.when_cond(0));
                for(int i=1;i<ctx.when_cond().size();i++)
                    whenexp+=" "+getOriginalCodeText(ctx.when_cond(i));
                if(ctx.then_keyword().SEMICOLON()!=null)
                    whenexp+=" ;";
                if(ctx.then_keyword().THEN()!=null)
                    whenexp+=" then";
                when.setCode(whenexp);
                addContextualProperty(when, ctx);
                addNodeAndPreEdge(when);
                preEdges.push(CFEdge.Type.TRUE);
                preNodes.push(when);
                if(ctx.statement_list_terms()!=null) {
                    CFNode now_end = END;
                    visit(ctx.statement_list_terms());
                    expNode(ctx.statement_list_terms());
                    END = now_end;
                }
                if(!preEdges.isEmpty())
                popAddPreEdgeTo(END);
                preEdges.push(CFEdge.Type.FALSE);
                preNodes.push(when);

            }
            else {
                if(ctx.else_tail().statement_list_terms()!=null) {
                    CFNode now_end = END;
                    visit(ctx.else_tail().statement_list_terms());
                    expNode(ctx.else_tail().statement_list_terms());
                    END = now_end;
                }
                if(!preEdges.isEmpty())
                popAddPreEdgeTo(END);
            }
            return null;
        }
        @Override
        public Void visitPrimaryBlockUnless(RubyParser.PrimaryBlockUnlessContext ctx)
        {
            CFNode  unless=new CFNode();
            unless.setLineOfCode(ctx.getStart().getLine());
            String unlessexpr="if "+getOriginalCodeText(ctx.expr());
            if(ctx.then_keyword().SEMICOLON()!=null)
                unlessexpr+=" :";
            if(ctx.then_keyword().THEN()!=null)
                unlessexpr+=" then";
            unless.setCode(unlessexpr);
            addContextualProperty(unless,ctx);
            if(preEdges.isEmpty())
            {
                cfg.addVertex(unless);
                cfg.addMethodEntry(unless);
            }
            else
            {
                addNodeAndPreEdge(unless);
            }
            preEdges.push(CFEdge.Type.FALSE);
            preNodes.push(unless);
            CFNode endunless = new CFNode();
            end=ctx.END().getSymbol().getLine();
            endunless.setLineOfCode(end);
            endunless.setCode("endunless");
            END=endunless;
            cfg.addVertex(END);
            if(ctx.statement_list_terms()!=null) {
                visit(ctx.statement_list_terms());
                expNode(ctx.statement_list_terms());
                END=endunless;
            }
            if(!preEdges.isEmpty())
            popAddPreEdgeTo(END);
            if(ctx.else_tail()==null)
            {
                cfg.addEdge(new Edge<>(unless, new CFEdge(CFEdge.Type.TRUE), END));
            }
            else
            {
                preEdges.push(CFEdge.Type.TRUE);
                preNodes.push(unless);
                if(ctx.else_tail().statement_list_terms()!=null) {
                    visit(ctx.else_tail().statement_list_terms());
                    expNode(ctx.else_tail().statement_list_terms());
                    END = endunless;
                }
                if(!preEdges.isEmpty())
                    popAddPreEdgeTo(END);
                //有没有必要设endif的循环边呢
            }
            Done=true;
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(END);
            return null;
        }
        @Override
        public Void visitNext(RubyParser.NextContext ctx)
        {
            CFNode next=new CFNode();
            next.setLineOfCode(ctx.getStart().getLine());
            next.setCode("next");
            addContextualProperty(next,ctx);
            addNodeAndPreEdge(next);
            NEXT.push(next);
            Done=true;
            return null;
        }
        @Override
        public Void visitPrimaryBlockIf(RubyParser.PrimaryBlockIfContext ctx)
        {
            int size=preEdges.size();

            CFNode ifNode = new CFNode();
            ifNode.setLineOfCode(ctx.getStart().getLine());
            String ifexpr="if "+getOriginalCodeText(ctx.expr());
            if(ctx.then_keyword().SEMICOLON()!=null)
                ifexpr+=" :";
            if(ctx.then_keyword().THEN()!=null)
                ifexpr+=" then";
            ifNode.setCode(ifexpr);
            addContextualProperty(ifNode, ctx);
            if(preEdges.isEmpty())
            {
                cfg.addVertex(ifNode);
                cfg.addMethodEntry(ifNode);
            }
            else
            {
                addNodeAndPreEdge(ifNode);
            }
            preEdges.push(CFEdge.Type.TRUE);
            preNodes.push(ifNode);
            CFNode endif = new CFNode();
            end=ctx.END().getSymbol().getLine();
            endif.setLineOfCode(end);
            endif.setCode("endif");
            END=endif;
            cfg.addVertex(END);
            if(ctx.statement_list_terms()!=null) {
                visit(ctx.statement_list_terms());
                expNode(ctx.statement_list_terms());
                END=endif;
            }
            if(!preEdges.isEmpty()) {
                popAddPreEdgeTo(END);
            }
            if(ctx.if_tail().isEmpty())
            {

                cfg.addEdge(new Edge<>(ifNode, new CFEdge(CFEdge.Type.FALSE), END));
            }
            else
            {
                    preEdges.push(CFEdge.Type.FALSE);
                    preNodes.push(ifNode);
                    for (int i = 0; i < ctx.if_tail().size(); i++) {         //这里有疑问，else后面不用加endif吗
                        visit(ctx.if_tail(i));
                    }
                                            //有没有必要设endif的循环边呢
            }
            END=endif;//防止嵌套结构导致end改变
            Done=true;
            preEdges.push(CFEdge.Type.EPSILON);
            preNodes.push(END);
            return null;
        }
        @Override
        public Void visitIf_tail(RubyParser.If_tailContext ctx)
        {
            if(ctx.ELSIF()!=null)
            {
                CFNode elseif =new CFNode();
                elseif.setLineOfCode(ctx.getStart().getLine());
                String elsif="if "+getOriginalCodeText(ctx.expr());
                if(ctx.then_keyword().SEMICOLON()!=null)
                    elsif+=" :";
                if(ctx.then_keyword().THEN()!=null)
                    elsif+=" then";
                elseif.setCode(elsif);
                addContextualProperty(elseif, ctx);
                addNodeAndPreEdge(elseif);
                preEdges.push(CFEdge.Type.TRUE);
                preNodes.push(elseif);

                if(ctx.statement_list_terms()!=null) {
                    CFNode now_end=END;
                    visit(ctx.statement_list_terms());
                    expNode(ctx.statement_list_terms());
                    END=now_end;
                }
                if(!preEdges.isEmpty())
                popAddPreEdgeTo(END);
                preEdges.push(CFEdge.Type.FALSE);
                preNodes.push(elseif);
            }
            else
            {
                if(ctx.else_tail().statement_list_terms()!=null) {
                    CFNode now_end = END;
                    visit(ctx.else_tail().statement_list_terms());
                    expNode(ctx.else_tail().statement_list_terms());
                    END = now_end;
                }
                if(!preEdges.isEmpty())
                popAddPreEdgeTo(END);
            }
            return null;
        }

        @Override
        public Void visitPrimaryStatementBreak(RubyParser.PrimaryStatementBreakContext ctx)
        {
            isBreak=true;
            CFNode breakNode =new CFNode();
            breakNode.setLineOfCode(ctx.getStart().getLine());
            breakNode.setCode("break");
            addContextualProperty(breakNode, ctx);
            addNodeAndPreEdge(breakNode);
//            preNodes.push(breakNode);
//            preEdges.push(CFEdge.Type.EPSILON);
//            popAddPreEdgeTo(forend);
            BREAK.push(breakNode);
            Done=true;
            return null;
        }
//        @Override
//        public Void visitPrimaryStatementRescue(RubyParser.PrimaryStatementRescueContext ctx)
//        {
//            CFNode resNode=new CFNode();
//            resNode.setLineOfCode(ctx.getStart().getLine());
//            resNode.setCode("rescue");
//            addContextualProperty(resNode,ctx);
//            cfg.addVertex(resNode);
//            for(int i=0;i< raise.size();i++)
//            {
//                cfg.addEdge(new Edge<>(raise.pop(), new CFEdge(CFEdge.Type.RAISE), resNode));
//            }
//            preNodes.push(resNode);
//            preEdges.push(CFEdge.Type.EPSILON);
//            if(ctx.rescure_param()!=null) {
//                CFNode params = new CFNode();
//                params.setLineOfCode(ctx.getStart().getLine());
//                params.setCode(getOriginalCodeText(ctx.rescure_param()));
//                addContextualProperty(params, ctx);
//                addNodeAndPreEdge(params);
//                preNodes.push(params);
//                preEdges.push(CFEdge.Type.EPSILON);
//            }
//            Done=true;
//            return null;
//        }
        @Override
        public Void visitPrimaryBeginBlock(RubyParser.PrimaryBeginBlockContext ctx)
        {
            CFNode bg=new CFNode();
            bg.setLineOfCode(ctx.getStart().getLine());
            bg.setCode("begin");
            addContextualProperty(bg,ctx);
            if (preEdges.isEmpty()) {
                cfg.addVertex(bg);
                cfg.addMethodEntry(bg);
            } else {
                addNodeAndPreEdge(bg);
            }
            preNodes.push(bg);
            preEdges.push(CFEdge.Type.EPSILON);

            CFNode endbegin=new CFNode();
            endbegin.setLineOfCode(0);
            endbegin.setCode("endbegin");
            END=endbegin;
            cfg.addVertex(END);
            visit(ctx.statement_list_terms(0));
            expNode(ctx.statement_list_terms(0));

            for(int i=0;i< raise.size();i++)
            {
                cfg.addEdge(new Edge<>(raise.pop(), new CFEdge(CFEdge.Type.THROWS   ), endbegin));
            }
            if(!preEdges.isEmpty())
                popAddPreEdgeTo(endbegin);
            END = endbegin;
            CFNode ensure=null;
            if(ctx.ensure()!=null) {
                preEdges.push(CFEdge.Type.EPSILON);
                preNodes.push(END);
                ensure=new CFNode();
                ensure.setLineOfCode(ctx.rescuestatement().else_tail().getStart().getLine());
                ensure.setCode("ensure");
                addContextualProperty(ensure,ctx);
                cfg.addVertex(ensure);
            }

//            if(ctx.ELSE()!=null) {
//                if(!preEdges.isEmpty())
//                popAddPreEdgeTo(END);
//                CFNode els=new CFNode();
//                els.setLineOfCode(ctx.ELSE().getSymbol().getLine());
//                els.setCode("else");
//                addContextualProperty(els,ctx);
//                addNodeAndPreEdge(els);
//                preNodes.push(els);
//                preEdges.push(CFEdge.Type.EPSILON);
//            }
            CFNode endwhetherthrow;
            if(ctx.rescuestatement()!=null)
            {
                endwhetherthrow=new CFNode();
                endwhetherthrow.setLineOfCode(0);
                endwhetherthrow.setCode("endwhetherthrow");
                cfg.addVertex(endwhetherthrow);
                CFNode rescue=new CFNode();
                rescue.setLineOfCode(ctx.rescuestatement().getStart().getLine());
                if(ctx.rescuestatement().rescure_param()!=null)
                    rescue.setCode("rescue"+getOriginalCodeText(ctx.rescuestatement().rescure_param()));
                else
                    rescue.setCode("rescue");
                addContextualProperty(rescue,ctx);
                cfg.addVertex(rescue);
                cfg.addEdge(new Edge<>(endbegin, new CFEdge(CFEdge.Type.THROWS), rescue));
                preNodes.push(rescue);
                preEdges.push(CFEdge.Type.EPSILON);
                visit(ctx.rescuestatement().statement_list_terms());
                expNode(ctx.rescuestatement().statement_list_terms());
//                CFNode endrescue=new CFNode();
//                endrescue.setLineOfCode(0);
//                endrescue.setCode("endrescue");
//                addNodeAndPreEdge(endrescue);
//                preEdges.push(CFEdge.Type.EPSILON);
//                preNodes.push(endrescue);
               // popAddPreEdgeTo(endbegin);
                if(ensure!=null)
                    popAddPreEdgeTo(ensure);
                else
                    popAddPreEdgeTo(endwhetherthrow);
                if(ctx.rescuestatement().else_tail()!=null)
                {
                    CFNode els=new CFNode();
                    els.setLineOfCode(ctx.rescuestatement().else_tail().getStart().getLine());
                    els.setCode("else");
                    addContextualProperty(els,ctx);
                    cfg.addVertex(els);
                    cfg.addEdge(new Edge<>(endbegin, new CFEdge(CFEdge.Type.NOT_THROWS), els));
                    preNodes.push(els);
                    preEdges.push(CFEdge.Type.EPSILON);
                    visit(ctx.rescuestatement().else_tail().statement_list_terms());
                    expNode(ctx.rescuestatement().else_tail().statement_list_terms());
//                    CFNode endels=new CFNode();
//                    endels.setLineOfCode(0);
//                    endels.setCode("endelse");
//                    addNodeAndPreEdge(endels);
//                    preEdges.push(CFEdge.Type.EPSILON);
//                    preNodes.push(endels);
                    //popAddPreEdgeTo(endbegin);
                    if(ensure!=null)
                        popAddPreEdgeTo(ensure);
                    else {
                        popAddPreEdgeTo(endwhetherthrow);

                    }
                }
                if(ensure==null) {
                    preEdges.push(CFEdge.Type.EPSILON);
                    preNodes.push(endwhetherthrow);
                }
            }

            if(ctx.ensure()!=null) {

                popAddPreEdgeTo(ensure);
                preNodes.push(ensure);
                preEdges.push(CFEdge.Type.EPSILON);
                visit(ctx.statement_list_terms(1));
                expNode(ctx.statement_list_terms(1));
                CFNode endensure=new CFNode();
                endensure.setLineOfCode(0);
                endensure.setCode("endensure");
                addNodeAndPreEdge(endensure);
                preEdges.push(CFEdge.Type.EPSILON);
                preNodes.push(endensure);
                END=endensure;
            }

            Done=true;
            return null;
        }
//        @Override
//        public Void visitExprPrint(RubyParser.ExprPrintContext ctx)
//        {
//            CFNode print = new CFNode();
//            print.setLineOfCode(ctx.getStart().getLine());
//            print.setCode(""+getOriginalCodeText(ctx.expr()));
//            addContextualProperty(print, ctx);
//        }
        @Override
        public Void visitPrimaryStatementRaise(RubyParser.PrimaryStatementRaiseContext ctx)
        {
            israise=true;
            CFNode raiseNode = new CFNode();
            raiseNode.setLineOfCode(ctx.getStart().getLine());
            raiseNode.setCode("raise"+getOriginalCodeText(ctx.expr()));
            addContextualProperty(raiseNode, ctx);

            raise.push(raiseNode);

//                CFNode expnode=new CFNode();
//                expnode.setLineOfCode(ctx.expr().getStart().getLine());
//                expnode.setCode(getOriginalCodeText(ctx.expr()));
//                addContextualProperty(expnode,ctx);
//                addNodeAndPreEdge(expnode);
//                preNodes.push(expnode);
//                preEdges.push(CFEdge.Type.EPSILON);

            addNodeAndPreEdge(raiseNode);
            Done=true;
            return null;
        }
        @Override
        public Void visitStatement_list_terms(RubyParser.Statement_list_termsContext ctx)
        {
            visitChildren(ctx);
            if(!Done)
            {
                if(ctx.statement()!=null) {
                    expr += getOriginalCodeText(ctx.statement());
                    if (ctx.terms().term().isEmpty()) {
                        expr += " ";
                    } else {
                        CFNode exp = new CFNode();
                        exp.setLineOfCode(ctx.terms().getStart().getLine());
                        exp.setCode(expr);
                        expr = "";
                        addContextualProperty(exp, ctx);
                        if (preEdges.isEmpty()) {
                            cfg.addVertex(exp);
                            cfg.addMethodEntry(exp);
                        } else {
                            addNodeAndPreEdge(exp);
                        }
                        preEdges.push(CFEdge.Type.EPSILON);
                        preNodes.push(exp);
                    }
                }
            }
            else
                Done=false;
            return null;
        }
        public void expNode(RubyParser.Statement_list_termsContext ctx)
        {
            if(!expr.equals("")) {
                CFNode exp = new CFNode();
                exp.setLineOfCode(ctx.terms().getStart().getLine());
                exp.setCode(expr);
                addContextualProperty(exp, ctx);
                if (preEdges.isEmpty()) {
                    cfg.addVertex(exp);
                    cfg.addMethodEntry(exp);
                } else {
                    addNodeAndPreEdge(exp);
                }
                preEdges.push(CFEdge.Type.EPSILON);
                preNodes.push(exp);
                expr="";
            }
        }
        private void addNodeAndPreEdge(CFNode node) {
            cfg.addVertex(node);
            popAddPreEdgeTo(node);
        }

        /**
         * Add a new edge to the given node, by poping the edge-type of the stack.
         */
        private void popAddPreEdgeTo(CFNode node) {
            if (dontPop)
                dontPop = false;
            else {
                Logger.debug("\nPRE-NODES = " + preNodes.size());
                Logger.debug("PRE-EDGES = " + preEdges.size() + '\n');
                cfg.addEdge(new Edge<>(preNodes.pop(), new CFEdge(preEdges.pop()), node));
            }
            //
            for (int i = casesQueue.size(); i > 0; --i)
                cfg.addEdge(new Edge<>(casesQueue.remove(), new CFEdge(CFEdge.Type.TRUE), node));
        }
        private  String getOriginalCodeText(ParserRuleContext ctx) {
            int start = ctx.start.getStartIndex();
            int stop = ctx.stop.getStopIndex();
            Interval interval = new Interval(start, stop);
            return ctx.start.getInputStream().getText(interval);
        }
        private class Block {

            public final String label;
            public final CFNode start, end;

            Block(CFNode start, CFNode end, String label) {
                this.start = start;
                this.end = end;
                this.label = label;
            }

            Block(CFNode start, CFNode end) {
                this(start, end, "");
            }
        }
    }
}
